# Kubernetes

Чтобы запустить наши приложения в кластере Kubernetes, нам нужно создать определенные ресурсы.
В конечном счете все, что мы делаем - это определяем нашу среду исполнения, используя Infrastructure-as-Code, аналогично тому, что мы делали с использованием файлов `Dockerfile`, но на этот раз для всей среды.

Сначала нам надо определить, сколько реплик наших отдельных образов приложений мы хотим запустить, как они подключены, настроены и т.д.
Все это хранится так же, как и программный код, который должен находиться в нашем проекте.

Давайте рассмотрим ресурсы, которые нам нужно создать.

## Pod-ы

Pod — это наименьший (базовый) модуль в Kubernetes. Это абстракция над нашим экземпляром приложения, которая содержит **_обычно_** один контейнер.
То есть каждый из контейнеров Docker, которые мы создали в прошлом разделе, теперь будет работать под управлением Kubernetes.
Если мы хотим иметь несколько копий наших приложений, мы создаем несколько pod-ов.

Ниже приведен фрагмент манифеста YAML, описывающего pod.
Пока создавать pod мы не будем, но давайте пройдемся по этому файлу, чтобы понять основные принципы.

```yaml
# ...
metadata:
  labels:
    app: coffee-shop
spec:
  containers:
  - name: coffee-shop
    image: de.icr.io/cee-<your-name>-workshop/coffee-shop:1
    ports:
    - containerPort: 9080
  restartPolicy: Always
# ...
```
Фрагмент выше определяет спецификацию одного pod-а, который содержит  контейнер, созданный из образа Docker coffee-shop.

Pod'ы в Kubernetes обычно не перезапускают. Если pod по какой-то причине останавливается, его уничтожают и создают заново.

Для того чтобы не пересоздавать Pod-ы вручную, мы используем контроллеры, которые следят за созданием необходимого количества копий Pod-ов - это Deployment-ы.

## Deployment-ы

Создадим Deployment - эта сущность управляет одной или несколькими репликами наших микросервисов.
Взгляните на конфигурацию Deployment:

```yaml
kind: Deployment
apiVersion: apps/v1
metadata:
  name: coffee-shop
spec:
  selector:
    matchLabels:
      app: coffee-shop
      version: v1
  replicas: 1
  template:
    metadata:
      labels:
        app: coffee-shop
        version: v1
    spec:
      containers:
      - name: coffee-shop
        image: de.icr.io/cee-<your-name>-workshop/coffee-shop:1
        ports:
        - containerPort: 9080
```

Этот Deployment выглядит аналогично описанию pod-а, приведенному ранее, но здесь pod описывается в секции template (шаблон), по которому будут создаваться новые реплики pod-а.
Deployment обеспечивает развертывание необходимого количества копий Pod-ов.
Если pod по какой-то причине остановился (специально или нет - не имеет значения), deployment автоматически сделает замену остановленному Pod-у, то есть создаст новую его копию.

Если мы хотим масштабировать наше приложение, изменить конфигурацию Deployment, или развернуть другую версию приложения (используя другой образ Docker), мы просто изменяем YAML-конфигурацию Deployment и обновляем его в кластере.
Kubernetes позаботится о том, чтобы такие изменения были правильно отработаны, с минимально возможным временем простоя.


## Тесты на жизнеспособность (liveness и readiness)

Для Pod-а, в котором работает приложение Java, потребуется несколько минут, чтобы он был полностью готов к работе. Поскольку Kubernetes ничего не знает о содержимом работающего контейнера, он может только предполагать, что работающие модули сразу могут обрабатывать входящие запросы.
Однако это конечно же не так, и поэтому мы должны каким-то образом сообщить, когда контейнер будет полностью готов делать что-то полезное.

С этой целью мы включаем сервисы жизнеспособности (liveness) и готовности (readiness) в deployment.

Сервис жизнеспособности сообщает Kubernetes, работает ли  модуль в целом (для нас это - сервер приложений).
Если это не так, Kubernetes немедленно остановит pod и заменит его на новую копию.
Проверка готовности сообщает, готов ли модуль для выполнения полезной работы, то есть обработки входящего трафика.

Существуют разные типы таких [сервисов контроля развертывания](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes).

Мы будем использовать функцию exec, которая позволяет выполнить произвольный скрипт внутри контейнера.
Команды `curl` будут подключаться к серверам приложений и ресурсам проверки работоспособности соответственно.

Взглянем на окончательную конфигурацию deployment:

```yaml
kind: Deployment
apiVersion: apps/v1
metadata:
  name: coffee-shop
spec:
  selector:
    matchLabels:
      app: coffee-shop
      version: v1
  replicas: 1
  template:
    metadata:
      labels:
        app: coffee-shop
        version: v1
    spec:
      containers:
      - name: coffee-shop
        image: de.icr.io/cee-<your-name>-workshop/coffee-shop:1
        ports:
        - containerPort: 9080
        livenessProbe:
          exec:
            command: ["sh", "-c", "curl -f http://localhost:9080/"]
          initialDelaySeconds: 20
        readinessProbe:
          exec:
            command: ["sh", "-c", "curl -s http://localhost:9080/health | grep -q coffee-shop"]
          initialDelaySeconds: 40
```

Файлы YAML с этим контентом надо ссоздать в папках `deployment/` двух проектов микросервисов.
Один deployment будет называться `coffee-shop` (то описание, которое мы только что обсудили выше), а другой - `barista`.
Убедитесь, что все имена, метки, изображения и URL указаны верно.

Теперь мы наконец создадим эти ресурсы в нашем кластере Kubernetes.
Для этого нужно просто применить эти файлы к конфигурации кластера командой `kubectl apply`:

```
kubectl apply -f coffee-shop/deployment/
kubectl apply -f barista/deployment/
```

Данная команда `apply` применит (создаст - если таких пока нет, или обновит - если уже есть), все ресурсы, которые находятся в соответствующем каталоге.

Вы можете проверить, были ли ресурсы созданы успешно, запросив текущий статус deployment-ов и pod-ов:

```
kubectl get pods
kubectl get deployments
```
Спустя несколько секунд вы должны увидеть два Pod-а `coffee-shop` и `barista`, которые готовы к работе, т.е. `READY: ... 2/2`.

Отлично! Наши приложения теперь работают в облаке, но как же к ним подключиться?

## Сервисы (Services)

Service Kubernetes - это логическая абстракция над «приложениями» (какими бы они ни были) и их репликами.
Service - это точка входа для нашего микросервиса.
Service-ы действуют как балансировщики нагрузки и распределяют запросы по экземплярам приложений.

Внутри Kubernetes кластера Service доступен через виртуальный IP-адрес или DNS имя - это позволяет нам легко обращаться к приложениям, используя понятные имена, как, например, `barista`, если в кластере есть service `barista`.

Давайте посмотрим на определение Service `coffee-shop`:

```yaml
kind: Service
apiVersion: v1
metadata:
  name: coffee-shop
  labels:
    app: coffee-shop
spec:
  selector:
    app: coffee-shop
  ports:
    - port: 9080
      name: http
  type: NodePort
```

Определение соодержит имя сервиса, метаданные по меткам и информацию по роутингу траффика (т.е. направить траффик на все Pod-ы, которые соответствуют данному selector-у).
Если вернетесь к манифесту Deployment, то увидите, что все модули содержат одну и ту же метку `app`. Это специальная метка, с помощью которой Service-ы решают, в какие Pod-ы нужно перенаправлять трафик.
Service определенный выше будет перенаправлять траффик на все Pod-ы с меткой `app: coffee-shop` через порт` 9080`.
Само собой, Service-ы работают только к рабочими Pod-ами.

Идем дальше. Создадим конфигурацию YAML для Service-ов `coffee-shop` и `barista` - в папке `deployment/`.
Вы можете создать новый файл - рядом с определением Deployment, или поместить все ресурсы Kubernetes в один YAML файл, в котором ресурсы (то есть объекты YAML) разделены линией из трех дефисов (`---`).
Так же, убедитесь, что имя, метка и определение selector-а в соответствующем Service-е соответствуют приложению coffee-shop или barista.

После того как вы добавили определения для Service-ов, создадим их в кластере, выполнив уже знакомые команды:

```
kubectl apply -f coffee-shop/deployment/
kubectl apply -f barista/deployment/
```

Это хороший пример концепции Infrastructure-as-Code - мы указываем желаемое состояние и просим Kubernetes _применить_ конфигурацию к нашему кластеру.

Проверим, были ли созданы Service-ы:

```
kubectl get services
```

## Обращение к приложениям

Теперь попробум подключиться к нашему приложению coffee-shop "снаружи" границ кластера.

Если мы создали кластер в облаке IBM с использованием аккаунта Lite, то подключиться к нашему приложению мы сможем через IP-адрес узла и порт сервиса. Вот так можно получить публичный IP-адрес нашего кластера:

```
ibmcloud ks workers --cluster cloud-native
ID         Public IP       Private IP      Machine Type   State    Status   Zone    Version   
kube-xxx   159.122.186.7   10.144.188.64   free           normal   Ready    mil01   1.10.12_1541   
```

И номер порта нашего приложения coffee-shop:

```
kubectl get service coffee-shop
NAME          TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
coffee-shop   NodePort   172.21.23.149   <none>        9080:30995/TCP   2m
```

В данном случае мы можем обратиться к нашему приложению coffee-shop используя URL `159.122.186.7:30995`, т.е. составив его из публичного IP адреса и NodePort сервиса:

```
curl <ip-address>:<node-port>/coffee-shop/resources/orders -i -XPOST \
  -H 'Content-Type: application/json' \
  -d '{"type":"Espresso"}'
 ```

> Если бы вы создали стандартный кластер (не из под аккаунта Lite), то вы могли бы воспользоваться Kubernetes ingress.
> 
>Однако, в этом семинаре, мы сосредоточимся на работе с сервисными сетками Istio и поэтому продемонстрируем работу Istio gateway (в следующем [разделе](04-istio.md)) вместо Kubernetes ingress.



## Kubernetes Config Maps (конфигурационные карты)

Мы можем определять переменные среды окружения непосредственно в манифесте развертывания приложения в Kubernetes или настраивать их в так называемых конфигурационных картах.

Конфигурационные карты это ресурсы Kubernetes, которые хранят конфигурационные параметры внутри кластера.

Они могут быть смапированны в файлы или, как в нашем примере, в переменные среды окружения.

Создадим следующее определение для Kubernetes:

```yaml
kind: ConfigMap
apiVersion: v1
metadata:
  name: coffee-config
data:
  location: CEE
```

Это создаст конфигурационную карту `coffee-config`, с параметром `location` и его значением `CEE`.

Для того чтобы значение этого параметра было доступно Pod-ам, мы добавим ссылку на конфигурационную карту в наш манифест Deployment приложения:

```yaml
# ...
containers:
- name: coffee-shop
  image: de.icr.io/cee-<your-name>-workshop/coffee-shop:1
  ports:
  - containerPort: 9080
  env:
  - name: location
    valueFrom:
      configMapKeyRef:
        name: coffee-config
        key: location
  livenessProbe:
# ...
```

В примере выше присваивается значение параметра `location` из конфигурационной карты `coffee-config` переменной окружения `location` внутри Pod-а.

Поскольку MicroProfile Config дает приложению конфигурационные параметры в виде переменных окружения, этот параметер `location` будет автоматически доступен приложению и значение для `location` будет `CEE`.

Давайе применим новые изменения в Deployment и посмотрим на месторасположение по конкретным заказам кофе. 
```
kubectl apply -f coffee-shop/deployment/
```

Cначала надо получить все заказы и уже по каждому из заказов посмотреть:
```
curl <ip-address>:<node-port>/coffee-shop/resources/orders
curl <ip-address>:<node-port>/coffee-shop/resources/orders/<order-uuid>
```


## 12 факторов

[12 Факторов](https://12factor.net/) современных Software-as-a-Service приложений описывают какие подходы и аспекты разработки должны быть применены при проектиовании таких приложений. 

Посмотрите на описанные факторы и оцените, где мы уже эти аспекты применили, используя Enterprise Java с облачными технологиями.

С помощью MicroProfile и его программной модели, в сочетании с Docker и Kubernetes, мы легко можем создавать 12-факторные микросервисы.

А на данный момент, мы настроили среду Kubernetes, которая управляет нашими микросервисами. Теперь давайте посмотрим как можно интегрировать Istio с нашим приложением в следующем [разделе](04-istio.md).
